//
// Created by hujin on 2023/3/10.
//

#ifndef COSI_OPERATIONS_H

#define COSI_OPERATIONS_H

#include "optim_engine.h"

#include "objectives.h"

template<typename T, typename I>
class OperationMergeL2 : public Operation<T, I, T, NodeStatL2<T>> {
    I id1, id2;

public:
    OperationMergeL2(I id_1, I id_2, Optimizer<T, I, T,  NodeStatL2<T>> &optim) : id1(id_1), id2(id_2) {
        this->value = Objectives<T>::merge((optim.node_stats[id1]), optim.node_stats[id2],
                                           optim.edge_map.biquery(id1, id2));
    }

    OperationMergeL2(I id_1, I id_2, T value) : id1(id_1), id2(id_2), Operation<T, I, T, NodeStatL2<T>>::value(value) {}

private:
    void operate(Optimizer<T, I, T,  NodeStatL2<T>> &optimizer) override {
        
        I id_new = optimizer.request_new_id();
        //T val = Objectives<T>::merge(node_stat_new, optimizer.node_stats[id_neighbor], merge);
        const NodeStatL2<T> &stat1 = optimizer.node_stats[id1];
//        const NodeStatL2<T> &stat1 = optimizer.node_stats[id1];
        auto node_stat_new = Update<T>::merge(stat1, (optimizer.node_stats[id2]), optimizer.edge_map.biquery(id1, id2));
        optimizer.edge_map.combine(id1, id2, id_new);
        optimizer.node_stats[id_new] = node_stat_new;
        optimizer.edge_map.iterate_neighbor(id_new, [&optimizer, &node_stat_new, &id_new](I id_neighbor, T merge) {
            T val = Objectives<T>::merge(node_stat_new, optimizer.node_stats[id_neighbor], merge);
            if (val > 0) {
                optimizer.notify(OperationMergeL2<T, I>(id_new, id_neighbor, val));
            }
        });
    }
};


#endif //COSI_OPERATIONS_H
